package bancosys.tec.rtm.diagnostician;

import java.io.Serializable;
import java.util.concurrent.Callable;

/**
 * A simple cache that holds onto the value for a time. Its value is calculated through the {@link Callable} passed in its constructor
 * 
 * @author takeshi
 * @param <T> the type of the object
 */
public class ExpiringCache<T> implements Serializable {

    private transient T referent;

    private long expirationTime;

    private final long durationMillis;

    private final Callable<T> creator;

    /**
     * C'tor
     * 
     * @param durationMillis the duration of the cache in ms
     * @param creator the {@link Callable} that creates the value
     */
    public ExpiringCache(long durationMillis, Callable<T> creator) {
        super();
        this.durationMillis = durationMillis;
        this.creator = creator;
    }

    /**
     * Gets the referent, calculating it if necessary
     * 
     * @return T
     */
    public synchronized T get() {
        this.checkExpiration();
        T ref = this.referent;
        if (ref != null) {
            return ref;
        }
        return this.renewReference();
    }

    /**
     * Checks if the value is expired and should be thrown away
     */
    private void checkExpiration() {
        if (System.currentTimeMillis() > this.expirationTime) {
            this.referent = null;
        }
    }

    /**
     * Renew the referent and also the expiration time
     * 
     * @return the referent that was just created
     */
    private T renewReference() {
        this.expirationTime = System.currentTimeMillis() + this.durationMillis;
        try {
            this.referent = this.creator.call();
        } catch (Exception e) {
            throw new IllegalStateException(e);
        }
        return this.referent;
    }

}
